/*
** Test 3D Engine by Neil C. Obremski                         [ Magenta's Maze ]
** 2017 Gibdon Moon Productions                   [ http://magsmaze.gibdon.com ]
**
** This is free and unencumbered software released into the public domain.
**
** Anyone is free to copy, modify, publish, use, compile, sell, or
** distribute this software, either in source code form or as a compiled
** binary, for any purpose, commercial or non-commercial, and by any
** means.
**
** In jurisdictions that recognize copyright laws, the author or authors
** of this software dedicate any and all copyright interest in the
** software to the public domain. We make this dedication for the benefit
** of the public at large and to the detriment of our heirs and
** successors. We intend this dedication to be an overt act of
** relinquishment in perpetuity of all present and future rights to this
** software under copyright law.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
** OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
** ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
** OTHER DEALINGS IN THE SOFTWARE.
**
** Tested w/ DOSBox 0.74, Microsoft C 5.10
 */
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include <math.h>

#include "mag-cga.h"
#include "mag-bios.h"
#include "mag-math.h"

#include "mag-3d.h"		/* need CGA before including */

/* models */
#include "mag-cube.h"
#include "mag-tree.h"

int main(int argc, char *argv[])
{
	int oldmode, scancode;
	int i, x = 0, y = 0, z = 100, xdeg = 0, ydeg = 0, zdeg = 0;
	char a;
	unsigned encircle = BLACK;
	struct mesh *mo = 0, *pmodel;
	struct vertex *v;
	int vi;
	int cy, cr;
	char alwaysshow = FALSE;
	char bigc = FALSE;

	init3d();

	pmodel = addmodel('?');
	reset(pmodel, A_WIRES, MAGENTA, CYAN);
	addpoly(pmodel, -30,-30,0, 0,30,0, 30,-30,0, 0);
	addpoly(pmodel, -90,-30,0, -60,30,0, -30,-30,0, 0);
	addpoly(pmodel, -60,40,0, 0,40,0, -30,-20,0, 0);
	normals(pmodel);

	if (argc > 1) {
		if (!stricmp(argv[1], "TREE")) {
			model_leaves(pmodel, 20, 30);
			pmodel->stroke = MAGENTA;
			pmodel->fill = CYAN;
		} else if (!stricmp(argv[1], "QUAD")) {
			reset(pmodel, A_QUADW, CYAN, MAGENTA);
			addquad(pmodel, -30,30,0, 30,30,0, 30,-30,0, -30,-30,0);
			normals(pmodel);
		} else if (!stricmp(argv[1], "CUBE")) {
			model_cube(pmodel, 25, 25, 25);
		} else if (!stricmp(argv[1], "PANEL")) {
			bigc = TRUE;
			model_panel(pmodel, 25, 25, 25);
		}
	}

	if (argc > 2) {
		FILE *fp = fopen(argv[2], "r");
		if (fp) {
			char textbuf[80];
			memset(textbuf, 0, sizeof(textbuf));
			for (y = 0; y < 16 && fgets(textbuf, 80, fp); y++) {
				for (x = 0; x < 16; x++) {
					if (textbuf[x] != ' ') {
						pmodel->texmap[y] |= (1 << x);
					}
				}
				memset(textbuf, 0, sizeof(textbuf));
			}
			fclose(fp);
		} else if (bigc) {
			texputc(pmodel, argv[2][0]);
		} else {
			texprint(pmodel, argv[2]);
		}
	} else {
		/* do test pattern  */
		for (y = 0; y < 16; y++) {
			for (x = 0; x < 16; x++) {
				if (y == x || !y || !x || 15 == y || 15 == x) {
					pmodel->texmap[y] |= (1 << x);
				}
			}
		}
	}

	/* calculate 3D circle offset and size */
	cy = 0x7FFF;
	cr = 0;
	MESH_FOREACH_VERT(pmodel,v,vi) {
		if (v->y < cy) {
			cy = v->y;
		}
		if (INT_ABS(v->x) > cr) {
			cr = v->x;
		}
		if (INT_ABS(v->z) > cr) {
			cr = v->z;
		}
	}
	cr += 20;

	y = 0; x = 20;
	a = pmodel->a;

	oldmode = screen(4);

	scancode = 0;

	do {
		freemesh(mo);
		mo = addmesh(pmodel, '0');

		if (72 == scancode) { /* up */
			xdeg -= 5;
			if (xdeg < 0) {
				xdeg = 355;
			}
		} else if (80 == scancode) { /* down */
			xdeg += 5;
			if (xdeg >= 360) {
				xdeg = 0;
			}
		} else if (75 == scancode) { /* left */
			ydeg += 5;
			if (ydeg >= 360) {
				ydeg = 0;
			}
		} else if (77 == scancode) { /* right */
			ydeg -= 5;
			if (ydeg < 0) {
				ydeg = 355;
			}
		} else if ('-' == (char)scancode) {
			VD--;
		} else if ('=' == (char)scancode) {
			VD++;
		} else if ('w' == (char)scancode) {
			z -= 10;
		} else if ('s' == (char)scancode) {
			z += 10;
		} else if ('a' == (char)scancode) {
			x -= 10;
		} else if ('d' == (char)scancode) {
			x += 10;
		} else if ('r' == (char)scancode) {
			y -= 10;
		} else if ('f' == (char)scancode) {
			y += 10;
		} else if (scancode >= '0' && scancode <= '9') {
			a = (char)(scancode - (int)'0');

		} else if ('`' == (char)scancode) {

		} else if ('c' == (char)scancode) {
			if (CYAN == encircle) {
				encircle = MAGENTA;
			} else if (MAGENTA == encircle) {
				encircle = WHITE;
			} else if (WHITE == encircle) {
				encircle = BLACK;
			} else {
				encircle = CYAN;
			}
		}

		if (xdeg > 0) {
			rotatex(mo, xdeg);
		}
		if (ydeg > 0) {
			rotatey(mo, ydeg);
		}
		if (zdeg > 0) {
			rotatez(mo, zdeg);
		}

		translate(mo, x, y, z);

		normals(mo);

		checkfaces(mo);

		mo->a = a;

		cls(BLACK);

		if (encircle) {
			circley3d(x, y + cy, z, cr, encircle);
		}

		locate(0,0);
		printf("XYZ (%3d,%3d,%3d)", x, y, z);
		locate(0,20);
		printf("ROT (%3d,%3d,%3d)", xdeg, ydeg, zdeg);
		locate(24,0);
		printf("KEY %2d, VD %d (%3d), FR=%d-%d",
			scancode, VD, 1<<VD,HITHER, YON);

		locate(1,0);
		for (i = 0; i < 10; i++) {
			printf("%c%d %s\n", (mo->a == (char)i ? '>' : ' '), i, A_NAMES[i]);
		}

		frustrum(mo);

		if (mo->shw) {
			project(mo, TRUE);
			render(mo);
		} else if (alwaysshow) {
			project(mo, FALSE);
			render(mo);
		} else {
			locate(24,38);
			putchar('!');
		}

	} while ('q' != (scancode = (char)getch()));

	screen(oldmode);

	printf("\nWorld Space:\n");
	dump(mo, FALSE);

	printf("Screen Space:\n");
	dump(mo, TRUE);

	printf("3d Memory: meshes=%d (*%d=%d bytes), models=%d (%d bytes)\n",
		MESH_MAX, sizeof(struct mesh), sizeof(meshes),
		MODEL_MAX, sizeof(models));
	printf("           %d bytes for vertices (%d per vertex)\n",
		MESH_VERT_MAX * sizeof(struct vertex), sizeof(struct vertex));
	printf("           %d bytes for points (%d per point)\n",
		MESH_VERT_MAX * sizeof(struct point), sizeof(struct point));


	quit3d();

	return 0;
}

